#include <mutex>
#include <map>
#include <queue>
#include <QThread>
#include "CStrCommandCenter.h"

static mutex s_lockq;

CStrCommandCenter::CStrCommandCenter()
{
	m_bRelease = false;

	m_pThread = new thread(DispatchCommand, this);
}

CStrCommandCenter::~CStrCommandCenter()
{
	m_bRelease = true;

	m_pThread->join();
}
void CStrCommandCenter::Subscribe(
	string strcmd,
	CStrCommandObserver* pob)
{
	lock_guard<mutex> lock(m_lock);

    qint64 uid = (qint64)pob;

	m_mapSubscribes[uid][strcmd] = true;

	m_mapkeyObs[strcmd][uid] = pob;
}

void CStrCommandCenter::Cancel(
	string strcmd,
	CStrCommandObserver* pob)
{
	lock_guard<mutex> lock(m_lock);

    qint64 uid = (qint64)pob;
	auto it = m_mapSubscribes.find(uid);
	if (it == m_mapSubscribes.end())
	{
		return;
	}
	it->second.erase(strcmd);

	auto itkey = m_mapkeyObs.find(strcmd);
	if (itkey == m_mapkeyObs.end())
	{
		return;
	}

	itkey->second.erase(uid);
}

void CStrCommandCenter::Cancel(
	CStrCommandObserver* pob)
{
	lock_guard<mutex> lock(m_lock);

    qint64 uid = (qint64)pob;

	auto itob = m_mapSubscribes.find(uid);
	for (auto itstr = itob->second.begin();
		itstr != itob->second.end();
		++itstr)
	{
		auto itkey = m_mapkeyObs.find(itstr->first);
		if (itkey != m_mapkeyObs.end())
		{
			itkey->second.erase(uid);
		}
	}

	m_mapSubscribes.erase(uid);
}

void CStrCommandCenter::DispatchCommand(CStrCommandCenter* pThis)
{
	while (!pThis->m_bRelease)
	{
		s_lockq.lock();
		if (pThis->m_cmds.size() == 0)
		{
			s_lockq.unlock();
            QThread::msleep(100);
			continue;
		}
        CLComCommand* pcmd = pThis->m_cmds.front();

		if (!pcmd)
		{
			s_lockq.unlock();
			continue;
		}
		pThis->m_cmds.pop();

		s_lockq.unlock();

        qint64 uid = (qint64)pcmd;
		string keystr = pcmd->GetCommandKey();

		auto itkeys = pThis->m_mapkeyObs.find(keystr);
		if (itkeys != pThis->m_mapkeyObs.end())
		{
			for (auto itob = itkeys->second.begin();
				itob != itkeys->second.end();
				++itob)
			{
				itob->second->CommandArrival(pcmd);
			}
		}
		auto itneedresult = pThis->m_mapNeedResult.find(uid);
		if (itneedresult != pThis->m_mapNeedResult.end())
		{
			if (itneedresult->second)
			{
				pThis->m_lock.lock();

				pThis->m_mapResults[uid] = pcmd;

				pThis->m_lock.unlock();
			}
			else
			{
				CCommandFactory::Giveback(pcmd);
			}
		}
		else
		{
			CCommandFactory::Giveback(pcmd);
		}
        QThread::msleep(100);
	}
}

qint64 CStrCommandCenter::PostCommand(
    CLComCommand& command,
	bool bNeedResult)
{
	lock_guard<mutex> lock(m_lock);

	string keystr = command.GetCommandKey();

	auto itkeys = m_mapkeyObs.find(keystr);
	if (itkeys == m_mapkeyObs.end())
	{
		return 0;
	}

    CLComCommand* pcmd = CCommandFactory::Allocate();

    qint64 uid = (qint64)pcmd;

	pcmd->Assignment(command.GetCommand());

	if (bNeedResult)
	{
		m_mapNeedResult[uid] = true;
	}
	else
	{
		m_mapNeedResult[uid] = false;
	}
	s_lockq.lock();
	m_cmds.push(pcmd);
	s_lockq.unlock();
	return uid;
}

bool CStrCommandCenter::GetCommandResult(
    qint64 id,
    CLComCommand& command)
{
	m_lock.lock();

	auto it = m_mapResults.find(id);
	if (it != m_mapResults.end())
	{
		command.Assignment(it->second->GetCommand());
		CCommandFactory::Giveback(it->second);

		m_mapResults.erase(it);
		m_mapNeedResult.erase(id);
		m_lock.unlock();
		return true;
	}
	m_lock.unlock();
	return false;
}
